#include "fs.h"
#include <unistd.h>
#include <minix/callnr.h>
#include "buf.h"
#include "file.h"
#include "fproc.h"
#include "inode.h"
#include "param.h"
#include "super.h"

#ifndef PATH_MAX
#include <limits.h>
#endif

#include <acl.h>

/* Este archivo define las llamadas al sistema para ACLs.
 * Consta de 2 puntos de entrada correspondientes a cada llamada:
 *  - do_aclget() copia el ACL de un archivo a un buffer de usuario
 *  - do aclset() guarda el ACL de un archivo desde un buffer de usuario
 */


/********** do_aclget() **************/

PUBLIC int do_aclget()
{
    int r1, r2, log_zone_size;
    unsigned i;
    
    register struct inode *inodo;
    block_t bloque_acl;
    struct buf* buf_bloque;
    
    short buf_acl[ITEMS_ACL];
    char fname[PATH_MAX];
    
    #if DEBUG
    printf("ACL: Llamaron a ACLGET!\n");
    #endif

    /* Leo el nombre del archivo */
    r1 = sys_copy(who, D, (phys_bytes) m.m1_p1,
                  FS_PROC_NR, D, (phys_bytes) fname,
                  (phys_bytes) PATH_MAX);

    if (r1 != OK) {
        #if DEBUG
        printf("ACL: No se pudieron copiar los datos del usuario!\n");
        #endif
        return ERR_SYS_COPY;
    }
    
    #if DEBUG
    printf("ACL: archivo %s\n", fname);
    #endif
    
    if ((inodo = eat_path(fname)) == NIL_INODE) {
        #if DEBUG
        printf("ACL: No se pudo abrir el inodo!\n");
        #endif;
        return ERR_INODO;
    }

    if ((inodo->i_mode & I_TYPE) == I_NAMED_PIPE) {
        #if DEBUG
        printf("ACL: Los pipes no soportan ACL!\n");
        #endif
        put_inode(inodo);
        return ERR_PIPE;
    }

    /* Solo el owner y el superusuario pueden usar el syscall */
    if (inodo->i_uid != fp->fp_effuid && !super_user) {
        #if DEBUG
        printf("ACL: Error de privilegios!\n");
        #endif
        put_inode(inodo);
        return ERR_PRIV;
    }

    /* Guardo la escala de bloque a zona para evitar
     * buscarla repetidas veces */
    log_zone_size = inodo->i_sp->s_log_zone_size;

    if (inodo->i_zone[ZONA_ACL] == NO_ZONE) {
        #if DEBUG
        printf("ACL: El inodo no tiene ACL\n");
        #endif
        put_inode(inodo);
        return INODO_SIN_ACL;
    }
    
    #if DEBUG
    printf("ACL: Accediendo a la ACL del inodo\n");
    #endif

    /* Obtengo los contenidos del bloque de ACL */
    bloque_acl = (block_t) inodo->i_zone[ZONA_ACL] << log_zone_size;
    buf_bloque = get_block(inodo->i_dev, bloque_acl, NORMAL);

    /* Copio la ACL al buffer para luego pasarlo al usuario */
    for(i = 0; i < ITEMS_ACL; i++) {
        buf_acl[i] = buf_bloque->b_acl[i];
    }

    /* Libero el bloque y el inodo */
    put_block(buf_bloque, FULL_DATA_BLOCK);
    put_inode(inodo);

    /* Copio a la memoria del usuario el ACL */
    r2 = sys_copy(FS_PROC_NR, D, (phys_bytes) buf_acl,
                  who, D, (phys_bytes) m.m1_p2,
                  (phys_bytes) BLOCK_SIZE);
    
    if (r2 != OK) {
        #if DEBUG
        printf("ACL: No se pudieron copiar los datos al usuario!\n");
        #endif
        return ERR_SYS_COPY;
    }

    return ACL_OK;
}



/********** do_aclset() **************/

PUBLIC int do_aclset()
{
    int r1, r2, log_zone_size;
    unsigned i;
    
    register struct inode *inodo;
    block_t bloque_acl;
    struct buf* buf_bloque;
    
    short buf_acl[ITEMS_ACL];
    char fname[PATH_MAX];
    
    #if DEBUG
    printf("ACL: Llamaron a ACLSET!\n");
    #endif


    /* Leo el nombre del archivo */
    r1 = sys_copy(who, D, (phys_bytes) m.m1_p1,
                  FS_PROC_NR, D, (phys_bytes) fname,
                  (phys_bytes) PATH_MAX);

    #if DEBUG
    printf("ACL: archivo %s\n", fname);
    #endif
    
    if((inodo = eat_path(fname)) == NIL_INODE) {
        #if DEBUG
        printf("ACL: No se pudo abrir el inodo!\n");
        #endif;
        return ERR_INODO;
    }
    
    if ((inodo->i_mode & I_TYPE) == I_NAMED_PIPE) {
        #if DEBUG
        printf("ACL: Los pipes no soportan ACL!\n");
        #endif
        put_inode(inodo);
        return ERR_PIPE;
    }
    
    /* Solo el owner y el superusuario pueden usar el syscall */
    if (inodo->i_uid != fp->fp_effuid && !super_user) {
        #if DEBUG
        printf("ACL: Error de privilegios!\n");
        #endif
        put_inode(inodo);
        return ERR_PRIV;
    }

    if(read_only(inodo) != OK) {
        #if DEBUG
        printf("ACL: El inodo es de solo lectura!\n");
        #endif
        put_inode(inodo);
        return ERR_INODO_RO;
    }
    
    /* Guardo la escala de bloque a zona para evitar
     * buscarla repetidas veces */
    log_zone_size = inodo->i_sp->s_log_zone_size; 

    /* Si el ACL pasado es NULL entonces borro el ACL
     * del archivo en cuestión. Caso contrario, lo leo
     * y lo escribo al disco. */
    
    if (m.m1_p2 == 0) {
        #if DEBUG
        printf("ACL: Borrando la ACL de %s\n", fname);
        #endif

        if (inodo->i_zone[ZONA_ACL] == NO_ZONE) {
            #if DEBUG
            printf("ACL: El inodo no tiene ACL!\n");
            #endif
        } 
        else {
            free_zone(inodo->i_dev, inodo->i_zone[ZONA_ACL]);
            inodo->i_zone[ZONA_ACL] = NO_ZONE;
            inodo->i_dirt = DIRTY;
        }
    }
    else {

        /* Leo el ACL nuevo de la memoria del usuario */
        r2 = sys_copy(who, D, (phys_bytes) m.m1_p2,
                      FS_PROC_NR, D, (phys_bytes) buf_acl,
                      (phys_bytes) BLOCK_SIZE);
        
        if (r1 != OK || r2 != OK) {
            #if DEBUG
            printf("ACL: No se pudieron leer los datos del usuario!\n");
            #endif
            put_inode(inodo);
            return ERR_SYS_COPY;
        }
        
        /* Si ya había una ACL, la sobreescribo */
        if (inodo->i_zone[ZONA_ACL] != NO_ZONE) {
            #if DEBUG
            printf("ACL: Sobreescribiendo el bloque existente de ACL\n");
            #endif
        }
        /* Si no había una ACL, pido una nueva zona en el
         * disco y almaceno ahi los datos obtenidos */
        else {
            #if DEBUG
            printf("ACL: Pidiendo una nueva zona para almacenar la ACL\n");
            #endif

            inodo->i_zone[ZONA_ACL] = alloc_zone(inodo->i_dev, inodo->i_zone[ZONA_ACL]);
            inodo->i_dirt = DIRTY;        
        }

        /* Copio los datos a la ACL y los salvo */
        bloque_acl = (block_t) inodo->i_zone[ZONA_ACL] << log_zone_size;
        buf_bloque = get_block(inodo->i_dev, bloque_acl, NORMAL);

        for(i = 0; i < ITEMS_ACL; i++) {
            buf_bloque->b_acl[i] = buf_acl[i];
        }
        
        buf_bloque->b_dirt = DIRTY;
        put_block(bloque_acl, FULL_DATA_BLOCK);
    }
    
    put_inode(inodo);
    return ACL_OK;
}


